#ifndef __WIZ_ALGEBRA_VECTOR_CORE_HPP__SHANHAOBO_19800429__
#define __WIZ_ALGEBRA_VECTOR_CORE_HPP__SHANHAOBO_19800429__

#include "../../Utils/WizAlgebraUtils.hpp"
#include "./WizAlgebraVectorBase.hpp"

namespace Wiz
{
    namespace Vector
    {
        namespace Core
        {
            template<class ElementT, int Num>
            struct Type : ::Wiz::Vector::Base::Type<ElementT, Num>
            {
                WIZ_DECLARE_CLASS_THIS(Type);
                //////////////////////////////////////////////////////////////////////////
                typedef ::Wiz::Vector::Base::Type<ElementT, Num>        tSuper;
                
                Type()
                {}
                Type(const tElement& InEle) : tSuper(InEle)
                {}
                Type(const tSuper& InOther) : tSuper(InOther)
                {}
                //////////////////////////////////////////////////////////////////////////

                //////////////////////////////////////////////////////////////////////////
                struct StaticMethod 
                {
                    //////////////////////////////////////////////////////////////////////////
                    WIZ_INLINE static tThis& Add(tThis& OutVec, tThisIn InVec1, tThisIn InVec2)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            OutVec.e[i] = InVec1.e[i] + InVec2.e[i];
                        }

                        return OutVec;
                    }
                    WIZ_INLINE static tThis& Add(tThis& OutVec, tThisIn InVec, const tElement& InEle)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            OutVec.e[i] = InVec.e[i] + InEle;
                        }

                        return OutVec;
                    }

                    WIZ_INLINE static tThis& AddAssign(tThis& InOutVec, tThisIn InVec)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            InOutVec.e[i] += InVec.e[i];
                        }

                        return InOutVec;
                    }
                    WIZ_INLINE static tThis& AddAssign(tThis& InOutVec, const tElement& InEle)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            InOutVec.e[i] += InEle;
                        }

                        return InOutVec;
                    }
                    //////////////////////////////////////////////////////////////////////////

                    //////////////////////////////////////////////////////////////////////////
                    WIZ_INLINE static tThis& Subtract(tThis& OutVec, tThisIn InVec1, tThisIn InVec2)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            OutVec.e[i] = InVec1.e[i] - InVec2.e[i];
                        }

                        return OutVec;
                    }
                    WIZ_INLINE static tThis& Subtract(tThis& OutVec, tThisIn InVec, const tElement& InEle)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            OutVec.e[i] = InVec.e[i] - InEle;
                        }

                        return OutVec;
                    }

                    WIZ_INLINE static tThis& SubtractAssign(tThis& InOutVec, tThisIn InVec)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            InOutVec.e[i] -= InVec.e[i];
                        }

                        return InOutVec;
                    }
                    WIZ_INLINE static tThis& SubtractAssign(tThis& InOutVec, const tElement& InEle)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            InOutVec.e[i] -= InEle;
                        }

                        return InOutVec;
                    }
                    //////////////////////////////////////////////////////////////////////////

                    //////////////////////////////////////////////////////////////////////////
                    WIZ_INLINE static tThis& Multiply(tThis& OutVec, tThisIn InVec1, tThisIn InVec2)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            OutVec.e[i] = InVec1.e[i] * InVec2.e[i];
                        }

                        return OutVec;
                    }
                    WIZ_INLINE static tThis& Multiply(tThis& OutVec, tThisIn InVec, const tElement& InEle)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            OutVec.e[i] = InVec.e[i] * InEle;
                        }

                        return OutVec;
                    }

                    WIZ_INLINE static tThis& MultiplyAssign(tThis& InOutVec, tThisIn InVec)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            InOutVec.e[i] *= InVec.e[i];
                        }

                        return InOutVec;
                    }
                    WIZ_INLINE static tThis& MultiplyAssign(tThis& InOutVec, const tElement& InEle)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            InOutVec.e[i] *= InEle;
                        }

                        return InOutVec;
                    }
                    //////////////////////////////////////////////////////////////////////////


                    //////////////////////////////////////////////////////////////////////////
                    WIZ_INLINE static tThis& Divide(tThis& OutVec, tThisIn InVec1, tThisIn InVec2)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            OutVec.e[i] = InVec1.e[i] / InVec2.e[i];
                        }

                        return OutVec;
                    }
                    WIZ_INLINE static tThis& Divide(tThis& OutVec, tThisIn InVec, const tElement& InEle)
                    {
                        if (::Wiz::GreaterZero(InEle))
                        {
                            const tElement RecipEle = 1 / InEle;
                            ::Wiz::Int::Type i;
                            for (i = 0; i < eCount; i++)
                            {
                                OutVec.e[i] = InVec.e[i] * RecipEle;
                            }
                        }

                        return OutVec;
                    }
                    WIZ_INLINE static tThis& Divide(tThis& OutVec, const tElement& InEle, tThisIn InVec)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            OutVec.e[i] = InEle / InVec.e[i];
                        }

                        return OutVec;
                    }

                    WIZ_INLINE static tThis& DivideAssign(tThis& InOutVec, tThisIn InVec)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            InOutVec.e[i] /= InVec.e[i];
                        }

                        return InOutVec;
                    }
                    WIZ_INLINE static tThis& DivideAssign(tThis& InOutVec, const tElement& InEle)
                    {
                        if (::Wiz::GreaterZero(InEle))
                        {
                            const tElement RecipEle = 1 / InEle;
                            ::Wiz::Int::Type i;
                            for (i = 0; i < eCount; i++)
                            {
                                InOutVec.e[i] *= RecipEle;
                            }
                        }

                        return InOutVec;
                    }
                    //////////////////////////////////////////////////////////////////////////

                    WIZ_INLINE static tElement Dot(tThisIn InVec1, tThisIn InVec2)
                    {
                        tElement OutSum = 0;
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            OutSum += InVec1.e[i] * InVec2.e[i];
                        }

                        return OutSum;
                    }

                    WIZ_INLINE static tElement AbsDot(tThisIn InVec1, tThisIn InVec2)
                    {
                        tElement OutSum = 0;
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            OutSum += ::Wiz::Abs(InVec1.e[i] * InVec2.e[i]);
                        }

                        return OutSum;
                    }

                    //////////////////////////////////////////////////////////////////////////

                    WIZ_INLINE static tElement Normalize(tThis& InOutVec)
                    {
                        const tElement Len = Length(InOutVec);

                        // Will also work for zero-sized Vectors, but will change nothing
                        if (::Wiz::GreaterZero(Len))
                        {
                            const tElement RecipLen = 1 / Len;
                            ::Wiz::Int::Type i;
                            for (i = 0; i < eCount; i++)
                            {
                                InOutVec.e[i] *= RecipLen;
                            }
                        }

                        return Len;
                    }

                    WIZ_INLINE static tElement Normalize(tThis& OutVec, tThisIn InVec)
                    {
                        const tElement Len = Length(InVec);

                        // Will also work for zero-sized Vectors, but will change nothing
                        if (::Wiz::GreaterZero(Len))
                        {
                            const tElement RecipLen = 1 / Len;
                            ::Wiz::Int::Type i;
                            for (i = 0; i < eCount; i++)
                            {
                                OutVec.e[i] = InVec.e[i] * RecipLen;
                            }
                        }

                        return Len;
                    }

                    //////////////////////////////////////////////////////////////////////////
                    WIZ_INLINE static tElement SqrLength(tThisIn InVec)
                    {
                        tElement OutSum = 0;
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            OutSum += ::Wiz::Sqr(InVec.e[i]);
                        }

                        return OutSum;
                    }
                    //////////////////////////////////////////////////////////////////////////

                    //////////////////////////////////////////////////////////////////////////
                    WIZ_INLINE static tElement Length(tThisIn InVec)
                    {
                        return ::Wiz::Sqrt(SqrLength(InVec));
                    }
                    //////////////////////////////////////////////////////////////////////////

                    //////////////////////////////////////////////////////////////////////////
                    WIZ_INLINE static tElement RecipLength(tThisIn InVec)
                    {
                        return ::Wiz::RecipSqrt(SqrLength(InVec));
                    }
                    //////////////////////////////////////////////////////////////////////////


                    //////////////////////////////////////////////////////////////////////////
                    WIZ_INLINE static tElement Distance(tThisIn InVec1, tThisIn InVec2)
                    {
                        tThis Vec;
                        Subtract(Vec, InVec1, InVec2);
                        return Length(Vec);
                    }
                    WIZ_INLINE static tElement SqrDistance(tThisIn InVec1, tThisIn InVec2)
                    {
                        tThis Vec;
                        Subtract(Vec, InVec1, InVec2);
                        return SqrLength(Vec);
                    }
                    ////////////////////////////////////////////////////////////////


                    ////////////////////////////////////////////////////////////////
                    WIZ_INLINE static ::Wiz::Bool::Type IsEqual(tThisIn InVec1, tThisIn InVec2)
                    {
                        ::Wiz::Int::Type i;
                        for (i = 0; i < eCount; i++)
                        {
                            if (::Wiz::NotEqual(InVec1.e[i], InVec2.e[i]))
                            {
                                return ::Wiz::Bool::False;
                            }
                        }

                        return ::Wiz::Bool::True;
                    }
                    ////////////////////////////////////////////////////////////////
                }; /// end of struct StaticMethod
            }; /// end of struct Type
        } /// end of namespace Core
    } /// end of namespace Vector
} /// end of namespace Wiz

#endif /*__WIZ_ALGEBRA_VECTOR_CORE_HPP__SHANHAOBO_19800429__*/
